home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / x11 / strategy / xvmahjon.por / xvmahjon / xvmahjongg / event.c next >
C/C++ Source or Header  |  1991-12-22  |  24KB  |  928 lines

  1. /*
  2.  * XView port of Mahjongg done by Stan K. Tazuma, 8/91-9/91
  3.  * Copyright 1991.
  4.  *
  5.  * $Header: /home/sirius/skt/cmds/src/sun/mj/RCS/event.c,v 2.5 91/12/22 17:12:37 skt Exp $
  6.  *
  7.  */
  8.  
  9. /*
  10.  *    Copyright 1988, Mark Holm
  11.  *            Exceptions
  12.  *
  13.  *    Acknowledgments to Dorothy Robinson for her artistic
  14.  *     abilities in drawing the icons and to Jim Batch for
  15.  *     technical support and graphical concepts (which I abandoned in favor
  16.  *       of the easy way out).
  17.  *
  18.  *    Permission is given to copy and distribute for non-profit purposes.
  19.  *
  20.  */
  21.  
  22. /*      This file has the event handlers for the background and
  23.  *       tiles in the play panel
  24.  */
  25.  
  26. #include <stdio.h>
  27. #include <sys/types.h>
  28. #include <xview/xview.h>
  29. #include <xview/panel.h>
  30. #include <xview/cursor.h>
  31.  
  32. #include "mahjongg.h"
  33.  
  34. /* external definitions */
  35.  
  36. extern Frame        main_frame;
  37. extern Panel        message_panel;
  38. extern Canvas        play_canvas;
  39. extern Xv_Window    Play_area;
  40. extern Panel_item    TL_hundred;
  41. extern Panel_item    TL_ten;
  42. extern Panel_item    TL_one;
  43. extern Panel_item    message;
  44. extern Panel_item    tile[144];
  45. extern Panel_item    board_num;
  46. extern Xv_Cursor    play_cursor;
  47.  
  48. extern Server_image    play_cursor_si;
  49. extern Server_image    stick_cursor_si;
  50. extern Server_image    confirm_cursor_si;
  51. extern Server_image    wait_cursor_si;
  52.  
  53. extern boolean        BandW;
  54. extern Tile        *board[144];
  55. extern int        tile_count;
  56. extern int        seed;
  57. extern Selected        selected[2];
  58.  
  59. extern Pixrect *Num_icon_mpr[];
  60. extern Server_image Num_icon_si[];
  61.  
  62. extern boolean Use_num_server_images;
  63. extern boolean Use_canvas_dynamic_cms;
  64. extern boolean Use_error_bells;
  65.  
  66. /* local globals */
  67.  
  68. Selected        undo_tiles[72][2];
  69. int            undo_count;        /* gets initialized to -1 in
  70.                          * mahjongg.c/build_image() */
  71. boolean            help_mode = FALSE;
  72. boolean            query_mode = FALSE;
  73.  
  74. void play_event_proc();
  75. void play_back_proc();
  76. void handle_event();
  77. void message_panel_event_proc();
  78.  
  79. boolean In_special_event_mode = FALSE;    /* use this to avoid unnecessary
  80.                      * setting or clearing of attributes */
  81.  
  82. /*******************************************/
  83.  
  84. void
  85. panel_msg(string, cursor_image, chg_event_handling)
  86. char        *string;
  87. Server_image    cursor_image;
  88. boolean        chg_event_handling;
  89. {
  90.     static char    *old_string = (char *) NULL;
  91.  
  92.     if (string) {
  93.     if (old_string == string) {
  94.         /*
  95.          * Save some CPU cycles and some client-to-server I/O
  96.          * by not updating the message line when it
  97.          * already shows what we want.  This would occur when help
  98.          * mode is being used, because there is a repeated sequence
  99.          * of calls to help_proc(), and it repeatedly calls this
  100.          * routine with the same parameters.
  101.          */
  102.         xv_set(message, XV_SHOW, TRUE, NULL);
  103.     }
  104.     else {
  105.         if (old_string)
  106.         xv_set(message, XV_SHOW, FALSE, NULL);
  107.  
  108.         xv_set(message, PANEL_LABEL_STRING,
  109.           string, XV_SHOW, TRUE, NULL);
  110.     }
  111.  
  112.     if (chg_event_handling) {
  113.         set_special_event_handling();
  114.     }
  115.     else {
  116.         clr_special_event_handling();
  117.     }
  118.     }
  119.     else {
  120.     xv_set(message, XV_SHOW, FALSE, NULL);
  121.  
  122.     /* always turn off special event processing if the string arg is 0 */
  123.  
  124.     clr_special_event_handling();
  125.     }
  126.  
  127.     xv_set(play_cursor, CURSOR_IMAGE, cursor_image, NULL);
  128.  
  129.     set_mj_cursor(play_cursor);
  130.  
  131.     old_string = string;
  132. }
  133.  
  134. set_special_event_handling()
  135. {
  136.     if (In_special_event_mode)
  137.     return;
  138.  
  139.     xv_set(message_panel,
  140.             PANEL_BACKGROUND_PROC,    play_back_proc,
  141.             WIN_IGNORE_EVENTS,
  142.                 WIN_ASCII_EVENTS,
  143.                 WIN_UP_EVENTS,
  144.                 LOC_DRAG,
  145.                 PANEL_EVENT_CANCEL,
  146.                 NULL,
  147.             NULL);
  148.     In_special_event_mode = TRUE;
  149. }
  150.  
  151. clr_special_event_handling()
  152. {
  153.     if (!In_special_event_mode)
  154.     return;
  155.  
  156.     xv_set(message_panel,
  157.             PANEL_BACKGROUND_PROC,    NULL,
  158.             WIN_CONSUME_EVENTS,
  159.                 WIN_ASCII_EVENTS,
  160.                 WIN_UP_EVENTS,
  161.                 LOC_DRAG,
  162.                 PANEL_EVENT_CANCEL,
  163.                 NULL,
  164.             NULL);
  165.     In_special_event_mode = FALSE;
  166. }
  167.  
  168. void
  169. help_proc()
  170. {
  171.     int         i;
  172.     Tile         *data[2];
  173.     static int    parse[2] = { 0, 0 };
  174.  
  175.     if (! (selected[0].in_preview_mode))
  176.     {
  177.     if (! (help_mode)) { /* Just starting. init and recall */
  178.         help_mode = TRUE;
  179.         parse[0] = 143;
  180.         parse[1] = 142;
  181.     }
  182.  
  183.     for ( ; parse[0] >= 0; parse[0]--)
  184.     {
  185.         if (!((board[parse[0]]->top_free &&            /* uncovered */
  186.         (board[parse[0]]->left_free || board[parse[0]]->right_free) && /* open */
  187.         (!(board[parse[0]]->removed))))) {  /* not already used */
  188.  
  189.         continue; /* not available go to next */
  190.  
  191.         }
  192.  
  193.         for(; parse[1] >= 0; parse[1]--) { /* check for second tile */
  194.  
  195.         if ((board[parse[0]]->value == board[parse[1]]->value) &&    /* right value */
  196.             (parse[0] != parse[1]) &&                    /* different item */
  197.             (board[parse[1]]->top_free &&                /* uncovered */
  198.             (board[parse[1]]->left_free || board[parse[1]]->right_free) && /* open */
  199.             (!(board[parse[1]]->removed)))) { /* not already used */
  200.  
  201.             /* Found a match, show it */
  202.  
  203.             /* flag found items */
  204.             selected[0].in_preview_mode = TRUE;
  205.             selected[1].in_preview_mode = TRUE;
  206.             selected[0].tileptr = board[parse[0]];
  207.             selected[1].tileptr = board[parse[1]];
  208.  
  209.             begin_preview(&selected[0]);
  210.             begin_preview(&selected[1]);
  211.  
  212.             panel_msg("Show next move? [Y] [] [N]", confirm_cursor_si,
  213.             TRUE);
  214.  
  215.             parse[1]--; /* do loop step */
  216.             return; /* all done this rotation */
  217.         }
  218.         } /* else go to next */
  219.  
  220.         parse[1] = parse[0] - 2; /* going around again */
  221.     }
  222.  
  223.     /* no more moves beep and show message */
  224.  
  225.     help_mode = FALSE;
  226.     mj_window_bell(main_frame);
  227.     panel_msg("No more moves.", play_cursor_si, FALSE);
  228.     }
  229.     else { /* search for available match */
  230.  
  231.     if (selected[1].in_preview_mode) { /* deselect last choice */
  232.  
  233.         /* cancel preview of selected tiles */
  234.         cancel_preview(&selected[1]);
  235.  
  236.         /* Clean up selected's variables */
  237.         selected[1].in_preview_mode = FALSE;
  238.     }
  239.  
  240.     if (!query_mode) {
  241.         query_mode = TRUE;
  242.         parse[0] = 143;
  243.     }
  244.  
  245.     data[0] = selected[0].tileptr;
  246.  
  247.     for(i = parse[0]; i >= 0; i--) {
  248.  
  249.         if ((board[i]->value == data[0]->value) && /* right value */
  250.         (board[i] != selected[0].tileptr) &&    /* different item */
  251.         (board[i]->top_free &&            /* uncovered */
  252.         (board[i]->left_free || board[i]->right_free) && /* open */
  253.         (!(board[i]->removed)))) { /* not already used */
  254.  
  255.         /* found one */
  256.  
  257.         selected[1].in_preview_mode = TRUE;
  258.         selected[1].tileptr = board[i];
  259.  
  260.         /* turn on preview */
  261.  
  262.         begin_preview(&selected[1]);
  263.  
  264.         /* set confirm message */
  265.  
  266.         panel_msg("Please confirm. [Y] [NEXT] [N]", confirm_cursor_si,
  267.             TRUE);
  268.  
  269.         /* return to sender */
  270.  
  271.         parse[0] = i - 1;
  272.         return;
  273.         }
  274.     }
  275.  
  276.     query_mode = FALSE;
  277.     selected[0].in_preview_mode = FALSE;
  278.     cancel_preview(&selected[0]);
  279.     mj_window_bell(main_frame);
  280.     panel_msg("No match.", play_cursor_si, FALSE);
  281.     }
  282. }
  283.  
  284. void
  285. remove_tiles(REMOVE)
  286. boolean    REMOVE;
  287. {
  288.     Tile *data[2];
  289.     int i;
  290.     int tiles_left_hun;
  291.     int tiles_left_ten;
  292.     int tiles_left_one;
  293.  
  294.     if (REMOVE) {
  295.     /* get data from items to be removed */
  296.     data[0] = selected[0].tileptr;
  297.     data[1] = selected[1].tileptr;
  298.     } else {
  299.     /* get data from items to be replaced */
  300.     data[0] = undo_tiles[undo_count][0].tileptr;
  301.     data[1] = undo_tiles[undo_count][1].tileptr;
  302.     }
  303.  
  304.     /* adjust adjacent tiles */
  305.     for(i = 0; i < 2 && data[0]->left_next[i] != DONT_CARE;
  306.         board[data[0]->left_next[i]]->right_free = REMOVE, i++);
  307.  
  308.     for(i = 0; i < 2 && data[1]->left_next[i] != DONT_CARE;
  309.         board[data[1]->left_next[i]]->right_free = REMOVE, i++);
  310.  
  311.     for(i = 0; i < 2 && data[0]->right_next[i] != DONT_CARE;
  312.         board[data[0]->right_next[i]]->left_free = REMOVE, i++);
  313.  
  314.     for(i = 0; i < 2 && data[1]->right_next[i] != DONT_CARE;
  315.         board[data[1]->right_next[i]]->left_free = REMOVE, i++);
  316.  
  317.     /* adjust covered tiles and images */
  318.     for (i = 0; i < 4 && data[0]->covered[i] != DONT_CARE; i++)
  319.     board[data[0]->covered[i]]->top_free = REMOVE;
  320.  
  321.     for (i = 0; i < 4 && data[1]->covered[i] != DONT_CARE; i++) 
  322.     board[data[1]->covered[i]]->top_free = REMOVE;
  323.  
  324.     if (data[0]->covered[0] != DONT_CARE && data[0]->covered[1] == DONT_CARE)
  325.     board[data[0]->covered[0]]->top_covered = ! REMOVE;
  326.     if (data[1]->covered[0] != DONT_CARE && data[1]->covered[1] == DONT_CARE)
  327.     board[data[1]->covered[0]]->top_covered = ! REMOVE;
  328.  
  329.     /* set removed flags */
  330.     data[0]->removed = REMOVE;
  331.     data[1]->removed = REMOVE;
  332.  
  333.     if (REMOVE) {
  334.     /* turn off preview */
  335.     /* use reverse order, otherwise it won't work in some cases */
  336.     cancel_preview(&selected[1]);
  337.     cancel_preview(&selected[0]);
  338.     } else  /* check to see if previewing an item and un-preview and select */
  339.     if (selected[0].in_preview_mode) {
  340.         cancel_preview(&selected[0]);
  341.         selected[0].in_preview_mode = FALSE;
  342.     }
  343.  
  344.     /* fix playing field */
  345.     if (REMOVE) {
  346.     clear_and_repaint();
  347.     }
  348.     else {
  349.     do_repaint();
  350.     }
  351.  
  352.     /* deselect tiles */
  353.     selected[0].in_preview_mode = FALSE;
  354.     selected[1].in_preview_mode = FALSE;
  355.  
  356.     /* fix tile counter */
  357.     tile_count += (REMOVE) ? -2 : 2;
  358.  
  359.     tiles_left_hun = tile_count / 100;
  360.     tiles_left_ten = (tile_count - (tiles_left_hun * 100)) / 10;
  361.     tiles_left_one = tile_count - (tiles_left_hun * 100) - (tiles_left_ten * 10);
  362.  
  363.     /* display hundreds tile by own status */
  364.     xv_set(TL_hundred, XV_SHOW, tiles_left_hun, 0);
  365.  
  366.     /* display tens tile by own status ored with hundreds status */
  367.     xv_set(TL_ten, XV_SHOW, tiles_left_hun || tiles_left_ten, 0);
  368.  
  369.     if (Use_num_server_images) {
  370.     xv_set(TL_ten, PANEL_LABEL_IMAGE, Num_icon_si[tiles_left_ten], 0);
  371.  
  372.     /* only need even tiles */
  373.     if ((tiles_left_one % 2) == 0)
  374.         xv_set(TL_one, PANEL_LABEL_IMAGE, Num_icon_si[tiles_left_one], 0);
  375.     }
  376.     else {
  377.     xv_set(TL_ten, PANEL_LABEL_IMAGE, Num_icon_mpr[tiles_left_ten], 0);
  378.  
  379.     /* only need even tiles */
  380.     if ((tiles_left_one % 2) == 0)
  381.         xv_set(TL_one, PANEL_LABEL_IMAGE, Num_icon_mpr[tiles_left_one], 0);
  382.     }
  383.  
  384.     if (REMOVE) {
  385.  
  386.     /* update undo_count */
  387.     undo_count++;
  388.  
  389.     /* update removed array */
  390.     undo_tiles[undo_count][0].in_preview_mode = TRUE;
  391.     undo_tiles[undo_count][0].tileptr = selected[0].tileptr;
  392.  
  393.     undo_tiles[undo_count][1].in_preview_mode = TRUE;
  394.     undo_tiles[undo_count][1].tileptr = selected[1].tileptr;
  395.  
  396.     /* remove confirm message */
  397.     panel_msg((char *) NULL, play_cursor_si, FALSE);
  398.  
  399.     /* check for clean board and congrat them */
  400.  
  401.     if ( tiles_left_hun == 0 && tiles_left_ten == 0 && tiles_left_one == 0) 
  402.         xv_set(message, PANEL_LABEL_STRING,
  403.             "Congratulations!! Press 'AGAIN' or 'NEW'",
  404.             XV_SHOW, TRUE,
  405.             0);
  406.  
  407.     } else { /* decrement undo_count */
  408.     undo_tiles[undo_count][0].in_preview_mode = FALSE;
  409.     undo_tiles[undo_count][1].in_preview_mode = FALSE;
  410.     undo_count--;
  411.     }
  412. }
  413.  
  414. void
  415. message_panel_event_proc(window, event)
  416. Xv_Window    window;
  417. Event        *event;
  418. {
  419.     static boolean event_was_button_press = FALSE;
  420.             /* if TRUE, then the event was a button press over
  421.              * a panel button.  (The only way this routine gets
  422.              * called is if one of the panel items which accepts
  423.              * events gets an event; at this point in time,
  424.              * that is either a button or a text item).
  425.              */
  426.     if (help_mode || query_mode) {
  427.     if (!event_is_down(event))
  428.         return;
  429.     
  430.     if (event_id(event) == MS_LEFT ||
  431.         event_id(event) == MS_MIDDLE ||
  432.         event_id(event) == MS_RIGHT
  433.         ) {
  434.         event_was_button_press = TRUE;
  435.         handle_event(window, event);
  436.         return;
  437.     }
  438.     /* ignore the event if we get here */
  439.     }
  440.     else {
  441.     if (event_was_button_press) {
  442.         /*
  443.          * We have to make sure we ignore the button release event
  444.          * that may or may not occur after turning events back on.
  445.          * This depends on whether the mouse cursor is over
  446.          * a panel button when the mouse button is released.
  447.          * Once we have ignored that particular button release event,
  448.          * all is well and we can go back to the normal operating mode.
  449.          * An easy way to accomplish this feat is to just ignore all
  450.          * up events until after we get a down event.  This scheme
  451.          * will work for both ASCII events or mouse events coming in.
  452.          */
  453.         if (!event_is_down(event))
  454.         return;
  455.  
  456.         event_was_button_press = FALSE;
  457.     }
  458.     panel_default_handle_event(window, event);
  459.     }
  460. }
  461.  
  462. void
  463. play_back_proc(window, event)
  464. Xv_Window    window;
  465. Event        *event;
  466. {
  467.     if (!event_is_down(event))
  468.     return;
  469.  
  470.     if (event_id(event) == MS_LEFT ||
  471.     event_id(event) == MS_MIDDLE ||
  472.     event_id(event) == MS_RIGHT
  473.     ) {
  474.  
  475.     handle_event(window, event);
  476.     }
  477. }
  478.  
  479. void
  480. handle_event(window, event)
  481. Xv_Window    window;
  482. Event        *event;
  483. {
  484.     if ((event_id(event) == MS_MIDDLE)
  485.         && selected[0].in_preview_mode && !help_mode) {
  486.  
  487.     help_proc();
  488.  
  489.     } else {
  490.  
  491.     query_mode = FALSE;
  492.  
  493.     if (selected[1].in_preview_mode) { /* doing confirm  or next help */
  494.  
  495.         switch (event_id(event)) {
  496.  
  497.         case MS_LEFT:
  498.         /* confirmed selection */
  499.         if (help_mode) { /* do next help */
  500.  
  501.             /* cancel preview of selected tiles */
  502.             /*
  503.              * use reverse order in order to deal with the case where
  504.              * the first tile selected is "adjacent" to the second tile
  505.              * selected, such that they visually overlap (this includes
  506.              * cases where one is on a higher level than the other)
  507.              */
  508.             cancel_preview(&selected[1]);
  509.             cancel_preview(&selected[0]);
  510.  
  511.             /* Clean up selected's variables */
  512.             selected[0].in_preview_mode = FALSE;
  513.             selected[1].in_preview_mode = FALSE;
  514.  
  515.             /* do next help */
  516.             help_proc();
  517.  
  518.         } else { /* confirmed selection. remove them */
  519.  
  520.             remove_tiles(TRUE);
  521.  
  522.         }
  523.         break;
  524.  
  525.         case MS_RIGHT:
  526.         /* refused selection */
  527.  
  528.         /* cancel preview of selected tiles */
  529.         /*
  530.          * use reverse order in order to deal with the case where
  531.          * the first tile selected is "adjacent" to the second tile
  532.          * selected, such that they visually overlap (this includes
  533.          * cases where one is on a higher level than the other)
  534.          */
  535.         cancel_preview(&selected[1]);
  536.         cancel_preview(&selected[0]);
  537.  
  538.         /* Clean up selected's variables */
  539.         selected[0].in_preview_mode = FALSE;
  540.         selected[1].in_preview_mode = FALSE;
  541.  
  542.         /* remove confirm message */
  543.         panel_msg((char *) NULL, play_cursor_si, FALSE);
  544.  
  545.         help_mode = FALSE;    /* may or may not be in help_mode at
  546.                      * this point, but make sure we
  547.                      * are not */
  548.         break;
  549.         }
  550.     }
  551.     }
  552. }
  553.  
  554. void
  555. play_event_proc(window, event)
  556. Xv_Window    window;
  557. Event        *event;
  558. {
  559.     Tile    *data;
  560.     int    value;
  561.     int    i = -1;
  562.  
  563.     if (!event_is_down(event))
  564.     return;
  565.  
  566.     if (    event_id(event) != MS_LEFT
  567.        &&    event_id(event) != MS_MIDDLE
  568.        &&    event_id(event) != MS_RIGHT) {
  569.  
  570.     return;
  571.     }
  572.  
  573.     /* check to see if in help_mode */
  574.  
  575.     if (help_mode || query_mode) {
  576.     play_back_proc(window, event);
  577.     return;
  578.     }
  579.  
  580.     /* check to see if just confirming */
  581.  
  582.     if (selected[1].in_preview_mode) {
  583.     play_back_proc(window, event);
  584.     return;
  585.     }
  586.  
  587.     /*
  588.      * The following used to be done only in play_back_proc(), back in the
  589.      * SunView version when both a PANEL_EVENT_PROC and a PANEL_BACKGROUND_PROC
  590.      * were specified as follows:
  591.      *                PANEL_EVENT_PROC, play_event_proc,
  592.      *                PANEL_BACKGROUND_PROC, play_back_proc,
  593.      * In the SunView version, a middle mouse button that occurred over
  594.      * a PANEL_BUTTON_IMAGE went to this play_event_proc routine, and
  595.      * eventually passed through to the switch statement that detects
  596.      * the MS_MIDDLE event.  If the
  597.      * middle mouse button occurred over blank space in the panel, then
  598.      * the PANEL_BACKGROUND_PROC (play_back_proc) must have been invoked,
  599.      * and the proper results occurred.
  600.      *
  601.      * Since the logic of this routine has been changed to kick out if the
  602.      * tile search doesn't find anything, I put the "if" below, which
  603.      * gives an early detection of a request for help.  I think this is
  604.      * better anyway.  Makes more sense to handle it right up front here,
  605.      * than do all the checks and stuff after here.
  606.      */
  607.  
  608.     if (    (event_id(event) == MS_MIDDLE)
  609.         && selected[0].in_preview_mode
  610.         && !help_mode) {
  611.     help_proc();
  612.     return;
  613.     }
  614.  
  615.     /*
  616.      * Clear the message line here because if in help_mode or query_mode,
  617.      * we don't need or want to clear the message line.  We only want to
  618.      * clear it after one of the messages I added where only a
  619.      * mj_window_bell() call used to be.  In those situations,
  620.      * we are no longer in help_mode.
  621.      */
  622.     xv_set(message, XV_SHOW, FALSE, NULL);
  623.  
  624.     if (event_id(event) != MS_LEFT)
  625.     return;
  626.  
  627.     /*
  628.      * Could put in code here to do a range check on the
  629.      * event_x(event) and event_y(event) coordinates, so that we don't
  630.      * waste time checking events beyond the valid range.  Could also
  631.      * add code to update the min and max, as tiles are removed.
  632.      * But this would add complexity to what is a simple search technique.
  633.      */
  634.  
  635.     i = find_tile(event_x(event), event_y(event));
  636.  
  637.     /* get data from item selected */
  638.  
  639.     if (i >= 0 && i <= 143)
  640.     data = board[i];
  641.     else {
  642.     return;        /* couldn't find any tile at that event location,
  643.              * so ignore the event and return */
  644.     }
  645.  
  646.     value = data->value;
  647.  
  648.     /* Left button down begin selection */
  649.     if ( data->top_free && (data->left_free || data->right_free)) {
  650.  
  651.     if (!(selected[0].in_preview_mode)) {
  652.  
  653.         /* fill first selection if empty */
  654.         selected[0].tileptr = board[i];
  655.         selected[0].in_preview_mode = TRUE;
  656.         begin_preview(&selected[0]);
  657.  
  658.     } else {
  659.  
  660.         if (board[i] == selected[0].tileptr) { /* deselect item */
  661.  
  662.         selected[0].in_preview_mode = FALSE;
  663.         cancel_preview(&selected[0]);
  664.  
  665.         } else {
  666.         data = selected[0].tileptr;
  667.         if ( value == data->value) {
  668.             /* fill second and show confirm message */
  669.  
  670.             selected[1].tileptr = board[i];
  671.             selected[1].in_preview_mode = TRUE;
  672.             begin_preview(&selected[1]);
  673.  
  674.             panel_msg("Please confirm. [Y] [] [N]",
  675.                 confirm_cursor_si, TRUE);
  676.         } else { /* beep at them */
  677.  
  678.             panel_msg("Not a valid match; try again.",
  679.                 play_cursor_si, FALSE);
  680.             mj_window_bell(main_frame);
  681.         }
  682.         }
  683.     }
  684.     } else { /* beep at them */
  685.     panel_msg("Tile not playable; try again.", play_cursor_si, FALSE);
  686.     mj_window_bell(main_frame);
  687.     }
  688. }
  689.  
  690. void
  691. quit_proc()
  692. {
  693.     xv_destroy_safe(main_frame);
  694.     exit(0);
  695. }
  696.  
  697. void
  698. new_proc()
  699. {
  700.     seed = random() % 20011;
  701.     build_image(FALSE);
  702.     place_tiles(FALSE);
  703. }
  704.  
  705. void
  706. again_proc()
  707. {
  708.     build_image(TRUE);
  709.     place_tiles(FALSE);
  710. }
  711.  
  712. void
  713. undo_proc()
  714. {
  715.     if (undo_count < 0) {
  716.     panel_msg("No more to undo.", play_cursor_si, FALSE);
  717.     mj_window_bell(main_frame);
  718.     }
  719.     else
  720.     remove_tiles(FALSE);
  721. }
  722.  
  723. void
  724. board_num_proc()
  725. {
  726.     sscanf((char *) xv_get(board_num, PANEL_VALUE), "%d", &seed);
  727.     build_image(FALSE);
  728.     place_tiles(FALSE);
  729. }
  730.  
  731. begin_preview(selptr)
  732. Selected *selptr;
  733. {
  734.     extern Pixrect *xv_mem_create();
  735.     static Pixrect *p = NULL;
  736.     Tile *tileptr = selptr->tileptr;
  737.     static boolean plane_mask_set = FALSE;
  738.  
  739.     if (BandW) {
  740.     if (! plane_mask_set) {
  741.         set_plane_mask(Play_area, 0x1);
  742.         plane_mask_set = TRUE;
  743.     }
  744.  
  745.     pw_rop(Play_area, tileptr->x_loc, tileptr->y_loc,
  746.         64, 64, PIX_NOT(PIX_SRC),
  747.         Play_area, tileptr->x_loc, tileptr->y_loc);
  748.     }
  749.     else if (Use_canvas_dynamic_cms) {
  750.     if (! plane_mask_set) {
  751.         set_plane_mask(Play_area, 0x7);
  752.         plane_mask_set = TRUE;
  753.     }
  754.  
  755.     pw_rop(Play_area, tileptr->x_loc, tileptr->y_loc,
  756.         64, 64, PIX_NOT(PIX_SRC),
  757.         Play_area, tileptr->x_loc, tileptr->y_loc);
  758.     }
  759.     else {
  760.     if (p == NULL) {
  761.         p = xv_mem_create(64, 64, 8);
  762.         if (p == NULL) {
  763.         fprintf(stderr, "begin_preview: xv_mem_create returned NULL\n");
  764.         return;
  765.         }
  766.     }
  767.     if (selptr->si_before_preview == NULL) {
  768.         selptr->si_before_preview =
  769.             (Server_image) xv_create(XV_NULL, SERVER_IMAGE,
  770.                 XV_WIDTH,        64,
  771.                 XV_HEIGHT,        64,
  772.                 SERVER_IMAGE_DEPTH,    8,
  773.                 SERVER_IMAGE_COLORMAP,    "mahjongg_canvas_cms",
  774.                 NULL);
  775.         if (selptr->si_before_preview == NULL) {
  776.         fprintf(stderr,
  777.             "begin_preview: error, couldn't create server image\n");
  778.         return;
  779.         }
  780.     }
  781.  
  782.     /* save the "before" image into a server image, for fast update
  783.      * if the user does a cancel on the tile */
  784.  
  785.     pw_read(selptr->si_before_preview, 0, 0, 64, 64, PIX_SRC,
  786.             Play_area, tileptr->x_loc, tileptr->y_loc);
  787.  
  788.     /* pull the "before" image into a local pixrect */
  789.     pw_read(p, 0, 0, 64, 64, PIX_SRC,
  790.             selptr->si_before_preview, 0, 0);
  791.  
  792.     convert_physical_to_logical_colors_pixrect(p);
  793.  
  794.     invert_pixrect(p);
  795.  
  796.     /* put back the modified pixrect */
  797.     pw_write(Play_area, tileptr->x_loc, tileptr->y_loc, 64, 64, PIX_SRC,
  798.             p, 0, 0);
  799.     }
  800. }
  801.  
  802. cancel_preview(selptr)
  803. Selected *selptr;
  804. {
  805.     Tile *tileptr = selptr->tileptr;
  806.  
  807.     if (BandW) {
  808.     pw_rop(Play_area, tileptr->x_loc, tileptr->y_loc,
  809.         64, 64, PIX_NOT(PIX_SRC),
  810.         Play_area, tileptr->x_loc, tileptr->y_loc);
  811.     }
  812.     else if (Use_canvas_dynamic_cms) {
  813.     pw_rop(Play_area, tileptr->x_loc, tileptr->y_loc,
  814.         64, 64, PIX_NOT(PIX_SRC),
  815.         Play_area, tileptr->x_loc, tileptr->y_loc);
  816.     }
  817.     else {
  818.     pw_write(Play_area, tileptr->x_loc, tileptr->y_loc, 64, 64, PIX_SRC,
  819.         selptr->si_before_preview, 0, 0);
  820.     }
  821. }
  822.  
  823. clear_and_repaint()
  824. {
  825.     /*
  826.      * This code clears just the two tiles that have been removed.
  827.      * The two tiles are the only areas that really have to be cleared
  828.      * for a proper update to the window, when the board is re-drawn.
  829.      * The result is a much smoother update to the screen than if
  830.      * the whole window is cleared.
  831.      */
  832.     pw_writebackground(Play_area,
  833.         selected[0].tileptr->x_loc, selected[0].tileptr->y_loc,
  834.         64, 64,
  835.         PIX_CLR);
  836.     pw_writebackground(Play_area,
  837.         selected[1].tileptr->x_loc, selected[1].tileptr->y_loc,
  838.         64, 64,
  839.         PIX_CLR);
  840.     do_repaint();
  841. }
  842.  
  843. /*
  844.  * local_mem_rop() is never called by any of the routines here.  It will
  845.  * be called by XView internal code, after the address of it is stuffed
  846.  * into an XView internal data structure.
  847.  *
  848.  * See the init_local_mem_rop() routine below for more info.
  849.  */
  850. local_mem_rop(dpr, dx, dy, width, height, op, spr, sx, sy)
  851. Pixrect *dpr;
  852. int dx, dy, width, height;
  853. int op;
  854. Pixrect *spr;
  855. int sx, sy;
  856. {
  857.     extern struct pixrectops mem_ops;
  858.  
  859.     /* we support a direct one-to-one copy of a memory
  860.      * pixrect to another memory pixrect */
  861.     if (op == PIX_SRC
  862.     && dpr->pr_ops == &mem_ops    /* is a memory pixrect */
  863.     && spr->pr_ops == &mem_ops    /* is a memory pixrect */
  864.     ) {
  865.  
  866.     /*
  867.     dpr->pr_ops = spr->pr_ops;
  868.     */
  869.     /* depth should be the same
  870.     dpr->pr_depth = spr->pr_depth;
  871.     */
  872.     /* don't change height and width
  873.     dpr->pr_height = height;
  874.     dpr->pr_width = width;
  875.     */
  876.     mpr_d(dpr)->md_linebytes = mpr_d(spr)->md_linebytes;
  877.     mpr_d(dpr)->md_offset = mpr_d(spr)->md_offset;
  878.     mpr_d(dpr)->md_primary = mpr_d(spr)->md_primary;
  879.     mpr_d(dpr)->md_flags = mpr_d(spr)->md_flags;
  880.     bcopy(PIXRECT_IMAGE_DATA_PTR(spr), 
  881.         PIXRECT_IMAGE_DATA_PTR(dpr),
  882.         (width * height * PIXRECT_IMAGE_DEPTH(spr))/8);
  883.     }
  884.     else {
  885.     fprintf(stderr, "local_mem_rop: NO-OP\n");
  886.     }
  887. }
  888.  
  889. /*
  890.  * This routine stuffs the address of a local function into an internal
  891.  * XView data structure.  This isn't necessarily the cleanest way to
  892.  * accomplish what I wanted, but it was the easiest.  Had to do it
  893.  * because while XView supports most of the code to read images from the
  894.  * display, it stops short by not providing the code for copying pixrects.
  895.  * The hooks are there, but instead of providing the code, XView
  896.  * puts in stub routines which tell the user that the feature is not
  897.  * supported.  So without this local_mem_rop stuff, pw_read()'s would
  898.  * not work.  When and if XView does support the code, this routine and
  899.  * also local_mem_rop() won't be needed.
  900.  */
  901. init_local_mem_rop()
  902. {
  903.     extern struct pixrectops mem_ops;
  904.  
  905.     mem_ops.pro_rop = local_mem_rop;
  906. }
  907.  
  908. /*
  909.  * Set up the canvas paint window with a bit mask of 'n' for drawing purposes.
  910.  * This allows us to do a pixmap color inversion entirely on the
  911.  * server, without reading the pixmap data back to the client side.
  912.  *
  913.  * This operation must be done on the canvas paint window, not the canvas.
  914.  */
  915. set_plane_mask(window, n)
  916. Xv_Window window;
  917. int n;
  918. {
  919.     pw_putattributes(window, &n);
  920. }
  921.  
  922. mj_window_bell(window)
  923. Xv_Window window;
  924. {
  925.     if (Use_error_bells)
  926.     window_bell(window);
  927. }
  928.